package com.rabbit.eap.system.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.rabbit.eap.framework.common.api.ApiResult;
import com.rabbit.eap.framework.common.enums.MsgEnum;
import com.rabbit.eap.framework.common.service.impl.BaseServiceImpl;
import com.rabbit.eap.framework.core.pagination.PageInfo;
import com.rabbit.eap.framework.core.pagination.Paging;
import com.rabbit.eap.framework.utils.LoginUtil;
import com.rabbit.eap.system.domain.TreeSelect;
import com.rabbit.eap.system.dto.SysMenuDTO;
import com.rabbit.eap.system.entity.SysMenu;
import com.rabbit.eap.system.entity.SysRoleMenu;
import com.rabbit.eap.system.entity.SysUser;
import com.rabbit.eap.system.enums.MenuStateEnum;
import com.rabbit.eap.system.mapper.SysMenuMapper;
import com.rabbit.eap.system.mapper.SysRoleMenuMapper;
import com.rabbit.eap.system.param.SysMenuPageParam;
import com.rabbit.eap.system.service.ISysMenuService;
import com.rabbit.eap.system.vo.SysMenuVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @projectName: eap
 * @packageName: com.rabbit.eap.system.service.impl
 * @className SysMenuServiceImpl
 * @author： rabbit
 * @version： 1.0
 * @since： 2020/06/06 23:34:03
 * <p>Description:
 * 菜单 服务实现类
 * </p>

 */
@Slf4j
@Service
public class SysMenuServiceImpl extends BaseServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Autowired

    private SysRoleMenuMapper sysRoleMenuMapper;

    /**
     * @param sysMenuDTO:sysMenuDTO数据传输对象
     * @throws Exception
     * @Description: 添加菜单
     * @methodName: saveSysMenu
     * @return: ApiResult
     * @date: 2020/06/06 23:34:03
     * @author: rabbit
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult saveSysMenu(SysMenuDTO sysMenuDTO) throws Exception {
        if (checkMenuNameUnique(sysMenuDTO)) {
            return ApiResult.fail(sysMenuDTO.getMenuName() + MsgEnum.IS_EXIST.getDesc());
        }
        SysMenu sysMenu = sysMenuDTO.convert(SysMenu.class);
        boolean bool = super.save(sysMenu);
        if (bool) {
            return ApiResult.ok(MsgEnum.ADD_SUCCESS.getDesc());
        } else {
            return ApiResult.fail(MsgEnum.ADD_FAIL.getDesc());
        }
    }

    /**
     * @param sysMenuDTO:sysMenuDTO数据传输对象
     * @throws Exception
     * @Description 修改菜单
     * @methodName: modifySysMenu
     * @return: ApiResult
     * @date: 2020/06/06 23:34:03
     * @author: rabbit
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult modifySysMenu(SysMenuDTO sysMenuDTO) throws Exception {
        if (checkMenuNameUnique(sysMenuDTO)) {
            return ApiResult.fail(sysMenuDTO.getMenuName() + MsgEnum.IS_EXIST.getDesc());
        }
        SysMenu sysMenu = sysMenuDTO.convert(SysMenu.class);
        boolean bool = super.updateById(sysMenu);
        if (bool) {
            return ApiResult.ok(MsgEnum.UPDATE_SUCCESS.getDesc());
        } else {
            return ApiResult.fail(MsgEnum.UPDATE_FAIL.getDesc());
        }
    }

    /**
     * @param menuId:菜单Id
     * @throws Exception
     * @Description 删除菜单
     * @methodName: removeSysMenu
     * @return: ApiResult
     * @date: 2020/06/06 23:34:03
     * @author: rabbit
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult removeSysMenu(Long menuId) throws Exception {
        if (ObjectUtil.isNull(menuId)) {
            return ApiResult.fail(MenuStateEnum.MENU_ID.getDesc() + MsgEnum.NOT_NULL.getDesc());
        }
        //判断是否存在子菜单
        if (hasChildByMenuId(menuId)) {
            return ApiResult.fail(MenuStateEnum.HAS_CHILD_MENU.getDesc());
        }
        //判断菜单是否已经分配
        if (checkMenuExistRole(menuId)) {
            return ApiResult.fail(MenuStateEnum.MENU_EXIST_ROLE.getDesc());
        }
        boolean bool = super.removeById(menuId);
        if (bool) {
            return ApiResult.ok(MsgEnum.DELETE_SUCCESS.getDesc());
        } else {
            return ApiResult.fail(MsgEnum.DELETE_FAIL.getDesc());
        }
    }

    /**
     * @param id: id
     * @throws Exception
     * @Description 根据Id获取菜单详情
     * @methodName: getSysMenuInfo
     * @return: ApiResult
     * @date: 2020/06/06 23:34:03
     * @author: rabbit
     */
    @Override
    public ApiResult getSysMenuById(Long id) throws Exception {
        if (ObjectUtil.isNull(id)) {
            return ApiResult.fail(MsgEnum.PARAM_NOT_NULL.getDesc());
        }
        SysMenu sysMenu = super.getById(id);
        SysMenuVO sysMenuVO = sysMenu.convert(SysMenuVO.class);
        return ApiResult.ok(sysMenuVO);
    }

    /**
     * @param sysMenuPageParam
     * @throws Exception
     * @Description 菜单分页列表
     * @methodName: getSysMenuPageList
     * @return: ApiResult
     * @date: 2020/06/06 23:34:03
     * @author: rabbit
     */
    @Override
    public ApiResult getSysMenuPageList(SysMenuPageParam sysMenuPageParam) throws Exception {
        Page<SysMenuVO> page = new PageInfo<>(sysMenuPageParam, OrderItem.desc("create_time"));
        IPage<SysMenuVO> iPage = sysMenuMapper.getSysMenuPageList(page, sysMenuPageParam);
        Paging<SysMenuVO> paging = new Paging<SysMenuVO>(iPage);
        return ApiResult.ok(paging);
    }

    /**
     * @param userId: 用户Id
     * @Description: 菜单分页列表
     * @methodName: selectMenuPermsByUserId
     * @return: java.util.Set<java.lang.String>
     * @date: 2020/6/27 21:12
     * @author: rabbit
     */
    @Override
    public Set<String> selectMenuPermsByUserId(Long userId) {
        List<String> perms = sysMenuMapper.selectMenuPermsByUserId(userId);
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms) {
            if (ObjectUtils.isNotEmpty(perm)) {
                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
            }
        }
        return permsSet;
    }

    /**
     * @param roleId: 角色Id
     * @Description: 根据角色获取菜单
     * @methodName: getRoleMenuTree
     * @return: com.rabbit.eap.framework.common.api.ApiResult
     * @date: 2020/6/29 21:21
     * @author: rabbit
     */
    @Override
    public ApiResult getRoleMenuTree(Long roleId) {
        Long userId = LoginUtil.getUserId();
        List<SysMenu> menuList = getMenuListByUserId(new SysMenu(), userId);
        List<Integer> integerList = getMenuListByRoleId(roleId);
        List<TreeSelect> treeSelectList = buildMenuTreeSelect(menuList);
        Map<String, Object> map = new HashMap<>(16);
        map.put("checkedKeys", integerList);
        map.put("menus", treeSelectList);
        return ApiResult.ok(map);
    }

    /**
     * @param sysMenu:
     * @Description: 获取菜单列表
     * @methodName: getSysMenuList
     * @return: com.rabbit.eap.framework.common.api.ApiResult
     * @date: 2020/6/29 21:22
     * @author: rabbit
     */
    @Override
    public ApiResult getSysMenuList(SysMenu sysMenu) {
        Long userId = LoginUtil.getUserId();
        List<SysMenu> sysMenuList = getMenuListByUserId(sysMenu, userId);
        return ApiResult.ok(sysMenuList);
    }

    /**
     * @param sysMenu:
     * @Description: 获取菜单下拉树列表
     * @methodName: getTreeMenu
     * @return: com.rabbit.eap.framework.common.api.ApiResult
     * @date: 2020/6/29 21:27
     * @author: rabbit
     */
    @Override
    public ApiResult getTreeMenu(SysMenu sysMenu) {
        Long userId = LoginUtil.getUserId();
        List<SysMenu> menuList = getMenuListByUserId(new SysMenu(), userId);
        List<TreeSelect> treeSelectList = buildMenuTreeSelect(menuList);
        return ApiResult.ok(treeSelectList);
    }

    /**
     * @param menuId: 菜单ID
     * @Description: 是否存在菜单子节点
     * @methodName: hasChildByMenuId
     * @return: boolean
     * @date: 2020/6/27 21:13
     * @author: rabbit
     */
    private boolean hasChildByMenuId(Long menuId) {
        int result = sysMenuMapper.selectCount(
                new QueryWrapper<SysMenu>().lambda().eq(SysMenu::getParentId, menuId)
        );
        return result > 0 ? true : false;
    }

    /**
     * @param menuId: 菜单Id
     * @Description: 查询菜单是否绑定角色
     * @methodName: checkMenuExistRole
     * @return: boolean
     * @date: 2020/6/27 21:31
     * @author: rabbit
     */
    private boolean checkMenuExistRole(Long menuId) {
        int result = sysRoleMenuMapper.selectCount(
                new QueryWrapper<SysRoleMenu>().lambda().eq(SysRoleMenu::getMenuId, menuId)

        );
        return result > 0 ? true : false;
    }

    /**
     * @param menu: menu对象
     * @Description: 校验菜单名称是否唯一
     * @methodName: checkMenuNameUnique
     * @return: boolean
     * @date: 2020/6/27 21:35
     * @author: rabbit
     */
    private boolean checkMenuNameUnique(SysMenuDTO menu) {
        Long menuId = ObjectUtil.isNull(menu.getId()) ? -1L : menu.getId();
        QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(SysMenu::getMenuName, menu.getMenuName())
                .eq(SysMenu::getParentId, menu.getParentId());
        SysMenu info = sysMenuMapper.selectOne(queryWrapper);
        if (ObjectUtil.isNotNull(info) && info.getId().longValue() != menuId.longValue()) {
            return true;
        }
        return false;
    }

    /**
     * @param menu:   菜单对象
     * @param userId: 用户Id
     * @Description: 根据用户查询系统菜单列表
     * @methodName: getMenuListByUserId
     * @return: java.util.List<com.rabbit.eap.system.entity.SysMenu>
     * @date: 2020/6/29 21:01
     * @author: rabbit
     */
    private List<SysMenu> getMenuListByUserId(SysMenu menu, Long userId) {
        List<SysMenu> menuList = Lists.newArrayList();
        menu.getParams().put("userId", userId);
        menuList = sysMenuMapper.getMenuListByUserId(menu);
        return menuList;
    }

    /**
     * @param roleId: 角色Id
     * @Description: 根据角色查询系统菜单列表
     * @methodName: getMenuListByRoleId
     * @return: java.util.List<java.lang.Integer>
     * @date: 2020/6/29 21:01
     * @author: rabbit
     */
    private List<Integer> getMenuListByRoleId(Long roleId) {
        List<Integer> list = Lists.newArrayList();
        list = sysMenuMapper.getMenuListByRoleId(roleId);
        return list;
    }

    /**
     * @param menus: 菜单列表
     * @Description: 构建前端所需要下拉树结构
     * @methodName: buildMenuTreeSelect
     * @return: java.util.List<com.rabbit.eap.system.domain.TreeSelect>
     * @date: 2020/6/29 21:08
     * @author: rabbit
     */
    private List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus) {
        List<SysMenu> menuTrees = buildMenuTree(menus);
        return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    /**
     * @param menus: 菜单列表
     * @Description: 构建前端所需要下拉树结构
     * @methodName: buildMenuTree
     * @return: java.util.List<com.rabbit.eap.system.entity.SysMenu>
     * @date: 2020/6/29 21:08
     * @author: rabbit
     */
    private List<SysMenu> buildMenuTree(List<SysMenu> menus) {
        List<SysMenu> sysMenuList = new ArrayList<SysMenu>();
        for (Iterator<SysMenu> iterator = menus.iterator(); iterator.hasNext(); ) {
            SysMenu sysMenu = (SysMenu) iterator.next();
            // 根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (sysMenu.getParentId() == 0) {
                recursionMenu(menus, sysMenu);
                sysMenuList.add(sysMenu);
            }
        }
        if (sysMenuList.isEmpty()) {
            sysMenuList = menus;
        }
        return sysMenuList;
    }

    /**
     * @param list:
     * @param sysMenu:
     * @Description: 递归列表
     * @methodName: recursionMenu
     * @return: void
     * @date: 2020/6/29 21:15
     * @author: rabbit
     */
    private void recursionMenu(List<SysMenu> list, SysMenu sysMenu) {
        // 得到子节点列表
        List<SysMenu> childList = getChildList(list, sysMenu);
        sysMenu.setChildren(childList);
        for (SysMenu tChild : childList) {
            if (hasChild(list, tChild)) {
                // 判断是否有子节点
                Iterator<SysMenu> it = childList.iterator();
                while (it.hasNext()) {
                    SysMenu n = (SysMenu) it.next();
                    recursionMenu(list, n);
                }
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t) {
        List<SysMenu> sysMenuList = new ArrayList<SysMenu>();
        Iterator<SysMenu> it = list.iterator();
        while (it.hasNext()) {
            SysMenu n = (SysMenu) it.next();
            if (n.getParentId().longValue() == t.getId().longValue()) {
                sysMenuList.add(n);
            }
        }
        return sysMenuList;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<SysMenu> list, SysMenu t) {
        return getChildList(list, t).size() > 0 ? true : false;
    }

    @Override
    public List<SysMenu> selectMenusByUser(SysUser sysUser) {
        List<SysMenu> menus = new LinkedList<SysMenu>();
        menus = sysMenuMapper.selectMenusByUserId(sysUser.getId());
        return menus;
    }

    @Override
    public ApiResult getSysMenuAll(SysMenu sysMenu) {
        QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(StringUtils.isNotBlank(sysMenu.getMenuName()), SysMenu::getMenuName, sysMenu.getMenuName())
                .eq(StringUtils.isNotBlank(sysMenu.getStatus()), SysMenu::getStatus, sysMenu.getStatus())
                .eq(StringUtils.isNotBlank(sysMenu.getMenuKey()), SysMenu::getMenuKey, sysMenu.getMenuKey())
                .orderByAsc(SysMenu::getSort);
        List<SysMenu> list = sysMenuMapper.selectList(queryWrapper);
        return ApiResult.ok(list);
    }

    /**
     * @Description: 获取角色菜单权限
     * @methodName: getRoleMenu
     * @param roleId: 角色id
     * @return: com.rabbit.eap.framework.common.api.ApiResult
     * @date: 2020/10/6 17:06
     * @author: rabbit
     *
     */
    @Override
    public ApiResult getRoleMenu(Long roleId) {
        if(ObjectUtil.isNull(roleId)){
            return ApiResult.fail(MsgEnum.PARAM_NOT_NULL.getDesc());
        }
       List<SysMenu> list  =  sysMenuMapper.getRoleMenu(roleId);
        return ApiResult.ok(list);
    }
}
